﻿using System;
using Outlook = Microsoft.Office.Interop.Outlook;
using Core = Microsoft.Office.Core;
using Folder = OMC.DataManagement.Folder;
using MenuItem = OMC.DataManagement.MenuItem;
using Manager = OMC.DataManagement.Manager;
using StringResources = OMC.Properties.StringResources;
using MyForm = OMC.UserInterface.MyForm;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Resources;
using System.Runtime.InteropServices;

namespace OMC.AddInHandling
{
    /// <summary>
    /// This class could manage all the things
    /// which are responsable of outlook actions.
    /// </summary>
    class AddInHandler : IDisposable
    {
        private Outlook.Explorer explorer;
        private Outlook.Accounts accounts;
        private Manager manager { get; set; }
        private Folder folder { get; set; }
        private String TheTextOfTheContextPopupMenu;
        private String TheTextOfContextMenuRefreshButton;
        private String TheTextofMailNotMoved;
        private String TheTextOfContextMenuSaveButton;
        private String TheTextOfSaveFileDialogFilter;
        private String TheTextOfSaveFileDialogTitle;
        private String TheTextOfContextMenuOptions;
        private List<String> defaultFolderList;
        private Core.CommandBarPopup popUpMenu;
        private Core.CommandBarButton button;
        private MyForm userInterfaceOptions;
        private int folderIndex = 1000;

        /// <summary>
        /// The constructor to create an object of this class.
        /// </summary>
        /// <param name="explorer">Outlook.Explorer type of the actual application</param>
        /// <param name="accounts">Outlook.Accounts of the actual application</param>
        /// <param name="manager">a manager object of class DataManagement.Manager</param>
        public AddInHandler(Outlook.Explorer explorer, Outlook.Accounts accounts, DataManagement.Manager manager)
        {
            this.explorer = explorer;
            this.accounts = accounts;
            this.manager = manager;
            this.Init();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources
                if (userInterfaceOptions != null)
                {
                    userInterfaceOptions.Dispose();
                    userInterfaceOptions = null;
                }
            }
        }


        /// <summary>
        /// This method inits standard resources and default folders.
        /// </summary>
        private void Init()
        {
            this.CreateDefaultFolderList();
            this.LoadResources();
        }

        /// <summary>
        /// This method loads the string resources of the text, which is shown in the user interface.
        /// There are language files based on the culture info. default file is "en"!
        /// </summary>
        /// <example>
        /// The cultureinfo convention, see:
        /// Source:http://msdn.microsoft.com/de-de/library/system.globalization.cultureinfo%28v=vs.80%29.aspx
        /// </example>
        private void LoadResources()
        {
            ResourceManager rm = StringResources.ResourceManager;
            //ContextMenu Strings
            TheTextOfTheContextPopupMenu = rm.GetString("ContextMenuPopupName");
            TheTextOfContextMenuRefreshButton = rm.GetString("ContextMenuRefresh");
            TheTextOfContextMenuOptions = rm.GetString("ContextMenuOption");
            TheTextOfContextMenuSaveButton = rm.GetString("ContextMenuSave");
            //SaveDialog Strigs
            TheTextOfSaveFileDialogFilter = rm.GetString("saveFileDialogFilter");
            TheTextOfSaveFileDialogTitle = rm.GetString("saveFileDialogTitle");
            //UserInfo Strings
            TheTextofMailNotMoved = rm.GetString("MailNotMoved");
        }

        /// <summary>
        /// This method creates a list of string items, which represent the default folders in
        /// outlook to exclude them in the calculation of the classification.
        /// </summary>
        private void CreateDefaultFolderList()
        {
            defaultFolderList = new List<String>();

            foreach (Outlook.OlDefaultFolders folderType in
                     Outlook.OlDefaultFolders.GetValues(typeof(Outlook.OlDefaultFolders)))
            {
                AddToDefaultFolderList(folderType);
            }
            defaultFolderList.Add("deleted items");
            defaultFolderList.Add("sent items");
        }

        /// <summary>
        /// This method adds the specified default folder to the list of folders.
        /// </summary>
        private void AddToDefaultFolderList(Outlook.OlDefaultFolders folderType)
        {
            Outlook.NameSpace nameSpace = explorer.Application.GetNamespace("MAPI");
            Outlook.MAPIFolder folder;
            try
            {
                folder = nameSpace.GetDefaultFolder(folderType);
                defaultFolderList.Add(folder.Name);
            }
            catch (COMException)
            {
                // this folderType does not exist; do nothing
            }
        }

        /// <summary>
        /// This method loads the mail data of all accounts into the referenced list
        /// which contain folders which contain mailcontents. The loaded maildata
        /// are "mirrored" in the list of folder items.
        /// </summary>
        /// <returns>a list of folders of outlook</returns>
        public List<Folder> LoadMailData()
        {
            List<Folder> folderList = new List<Folder>();
            foreach (Outlook.Store store in explorer.Session.Stores)
            {
                Outlook.Folder RootFolder = store.GetRootFolder() as Outlook.Folder;
                CreateMailData(RootFolder, folderList);
            }
            return folderList;
        }

        /// <summary>
        /// This is a recursiv method, to create MailContents of the MailItems of
        /// all accounts. A few default folders are ignored, such as
        /// inbox, outbox, deleted items..
        /// </summary>
        /// <param name="rootFolder">the actual rootFolder</param>
        /// <param name="folderList">the referenced list of folder items</param>
        private void CreateMailData(Outlook.Folder rootFolder, List<Folder> folderList)
        {
            Outlook.Folders childFolders = rootFolder.Folders;
            if (childFolders.Count > 0)
            {
                foreach (Outlook.Folder childFolder in childFolders)
                {
                    if (ChekFolder(childFolder))
                    {
                        //Check, if childFolder contains any elements
                        if (childFolder.Items.Count > 0)
                        {
                            //inAccount.DisplayName = Account Name
                            folder = new Folder(childFolder.Name, folderIndex.ToString(), childFolder.EntryID);
                            //Utils.Debug.print("Ordner: " + TheChildFolder.Name);
                            foreach (object oItem in childFolder.Items)
                            {
                                if (oItem is Outlook.MailItem)
                                {
                                    Outlook.MailItem item = oItem as Outlook.MailItem;
                                    folder.AddMailContent(item.Body, item.Subject, item.SenderEmailAddress, item.To);
                                }
                            }
                            folderList.Add(folder);
                            folderIndex++;
                        }
                        CreateMailData(childFolder, folderList);
                    }
                    if (CheckIsInbox(childFolder))
                    {
                        CreateMailData(childFolder, folderList);
                    }
                }
            }
        }

        /// <summary>
        /// This method checks if the folder is type of Inbox Folder.
        /// </summary>
        /// <param name="folder">folder to check</param>
        /// <returns>true if is inbox, else false</returns>
        private bool CheckIsInbox(Outlook.Folder folder)
        {
            if (folder.Name.ToLower() ==
                explorer.Application.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Name.ToLower()
                || folder.Name.ToLower() == "inbox")
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// This method checks the folder default item type and if the folder is type of default folders.
        /// </summary>
        /// <param name="folder">in folder to check</param>
        /// <returns>
        /// true, if folder could be used
        /// false, if folder schould be banned
        /// </returns>
        private bool ChekFolder(Outlook.Folder folder)
        {
            if (folder.DefaultItemType == Outlook.OlItemType.olMailItem)
            {
                foreach (String DefaultFolderName in defaultFolderList)
                {
                    if (folder.Name.ToLower() == DefaultFolderName.ToLower())
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// This method moves mails from a folder to another folder.
        /// </summary>
        /// <param name="mailItem">the MailItem, which should be moved</param>
        /// <param name="folderName">the destination folder of the MailItem</param>
        /// <param name="folderExtra">extra info of the folder, like the entryID</param>
        public void MoveMailItem(Outlook.MailItem mailItem, String folderName, String folderExtra)
        {
            foreach (Outlook.Store store in explorer.Session.Stores)
            {
                Outlook.Folder root = store.GetRootFolder() as Outlook.Folder;
                SearchFolderAndMove(root, folderName, mailItem, folderExtra);
            }
        }

        /// <summary>
        /// This method is able to search the folder in the root node and move the mail item to them.
        /// </summary>
        /// <param name="folder">folder, which could contain the possible searched folder</param>
        /// <param name="folderName">the name of the searched folder</param>
        /// <param name="mailItem">the mail item to move</param>
        /// <param name="folderExtra">extra info of the folder, like the entryID</param>
        public void SearchFolderAndMove(Outlook.Folder folder, String folderName,
                                        Outlook.MailItem mailItem, String folderExtra)
        {
            foreach (Outlook.Folder tempFolder in folder.Folders)
            {
                if (tempFolder.Name == folderName && tempFolder.EntryID == folderExtra)
                {
                    try
                    {
                        mailItem.Move(tempFolder);
                    }
                    catch
                    {
                        MessageBox.Show("Mail wurde nicht verschoben");
                    }
                }
                SearchFolderAndMove(tempFolder, folderName, mailItem, folderExtra);
            }
        }

        /// <summary>
        /// This method creates a contextmenu item, which is type of popup menu.
        /// </summary>
        /// <param name="cmdBar">the command bar of the right clicked item</param>
        public void MakeContextPopUp(Core.CommandBar cmdBar)
        {
            //Create PopUpMenu in the ContextMenu
            popUpMenu = cmdBar.Controls.Add(Core.MsoControlType.msoControlPopup,
                                                     Type.Missing,
                                                     Type.Missing,
                                                     Type.Missing, true) as Core.CommandBarPopup;
            popUpMenu.BeginGroup = true;
            popUpMenu.Caption = TheTextOfTheContextPopupMenu;
            popUpMenu.Enabled = false;

            //set PopUpMenu Visible
            popUpMenu.Visible = true;
        }

        /// <summary>
        /// This Methode could be use to create the Context menu of
        /// each Mailitem with the assoziated Folder, classified by Weka.
        /// </summary>
        /// <example>
        /// Source CommandBar:  http://support.microsoft.com/kb/303018
        /// Source FaceID:      http://www.kebabshopblues.co.uk/2007/01/04/visual-studio-2005-tools-for-office-commandbarbutton-faceid-property/
        /// </example>
        /// <param name="cmdBar">this Commandbar could be modified</param>
        /// <param name="menuItemList">a list, which contains the classified folder names of the selected mail item</param>
        /// <param name="mailItem">the selected mail item</param>
        public void MakeContextMenuButtons(Core.CommandBar cmdBar, List<MenuItem> menuItemList, Outlook.MailItem mailItem)
        {
            foreach (MenuItem menuItem in menuItemList)
            {
                //Create Buttons to the PopUpMenu
                button = popUpMenu.Controls.Add(Core.MsoControlType.msoControlButton,
                                                    Type.Missing,
                                                    Type.Missing,
                                                    Type.Missing, true) as Core.CommandBarButton;
                button.Style = Core.MsoButtonStyle.msoButtonIconAndWrapCaption;
                button.FaceId = 0133;
                button.Caption = String.Format("{0} ({1:F1} %)", menuItem.FolderName, menuItem.Probability);

                //Set delegate to the ClickEventHandler
                String folderName = menuItem.FolderName;
                String folderExtra = menuItem.FolderExtraInfo;
                button.Click += new Core._CommandBarButtonEvents_ClickEventHandler(
                        (Core.CommandBarButton Cmd, ref bool Can) => TheButton_Click(Cmd, ref Can, mailItem, folderName, folderExtra));
                button.Visible = true;
            }

            //Create Refresh Buttons to the PopUpMenu
            button = popUpMenu.Controls.Add(Core.MsoControlType.msoControlButton,
                                                Type.Missing,
                                                Type.Missing,
                                                Type.Missing, true) as Core.CommandBarButton;
            button.BeginGroup = true;
            button.Style = Core.MsoButtonStyle.msoButtonIconAndWrapCaption;
            button.FaceId = 0037;
            button.Caption = TheTextOfContextMenuRefreshButton;

            //Set delegate to the ClickEventHandler
            button.Click += new Core._CommandBarButtonEvents_ClickEventHandler(TheRefreshButton_Click);
            button.Visible = true;

            //Create Save to ARFF Buttons to the PopUpMenu
            button = popUpMenu.Controls.Add(Core.MsoControlType.msoControlButton,
                                                Type.Missing,
                                                Type.Missing,
                                                Type.Missing, true) as Core.CommandBarButton;
            button.Style = Core.MsoButtonStyle.msoButtonIconAndWrapCaption;
            button.FaceId = 0003;
            button.Caption = TheTextOfContextMenuSaveButton;

            //Set delegate to the ClickEventHandler
            button.Click += new Core._CommandBarButtonEvents_ClickEventHandler(TheSaveButton_Click);
            button.Visible = true;

            //Create Option Buttons to the PopUpMenu
            button = popUpMenu.Controls.Add(Core.MsoControlType.msoControlButton,
                                                Type.Missing,
                                                Type.Missing,
                                                Type.Missing, true) as Core.CommandBarButton;
            button.BeginGroup = true;
            button.Style = Core.MsoButtonStyle.msoButtonIconAndWrapCaption;
            button.FaceId = 0439;
            button.Caption = TheTextOfContextMenuOptions;

            //Set delegate to the ClickEventHandler
            button.Click += new Core._CommandBarButtonEvents_ClickEventHandler(TheOptionsButton_Click);
            button.Visible = true;

            //Set PopUpMenu enabled
            popUpMenu.Enabled = true;
        }

        /// <summary>
        /// The method, which is called by a button events handler.
        /// </summary>
        /// <param name="cmdBarButton">the commandbar button, which called this method</param>
        /// <param name="Cancel">cancel</param>
        /// <param name="mailItem">the mail item to move</param>
        /// <param name="folderName">the folder name of the destination folder of the mail item</param>
        /// /// <param name="folderExtra">extra info of the folder, like the entryID</param>
        private void TheButton_Click(Core.CommandBarButton cmdBarButton, ref bool Cancel,
                                     Outlook.MailItem mailItem, String folderName, String folderExtra)
        {
            MoveMailItem(mailItem, folderName, folderExtra);
        }

        /// <summary>
        /// The method, which is called by the button handler of the refresh button.
        /// (it could be used, to refresh manually the dataset)
        /// </summary>
        /// <param name="cmdBarButton">the commandbar button, which called this method</param>
        /// <param name="Cancel">cancel</param>
        private void TheRefreshButton_Click(Core.CommandBarButton cmdBarButton, ref bool Cancel)
        {
            if (manager.IsDataSetLoaded())
            {
                manager.Load();
            }
        }

        /// <summary>
        /// This method opens a form to customize properties.
        /// </summary>
        /// <param name="cmdBarButton">the commandbar button, which called this method</param>
        /// <param name="Cancel">cancel</param>
        private void TheOptionsButton_Click(Core.CommandBarButton cmdBarButton, ref bool Cancel)
        {
            userInterfaceOptions = new MyForm(manager);
            userInterfaceOptions.Show();
        }

        /// <summary>
        /// The method, which is called by the button handler of the save button.
        /// (it could be used, to save the data to a arff file)
        /// </summary>
        /// <param name="cmdBarButton">the commandbar button, which called this method</param>
        /// <param name="Cancel">cancel</param>
        private void TheSaveButton_Click(Core.CommandBarButton cmdBarButton, ref bool Cancel)
        {
            popUpMenu.Enabled = false;

            // Displays a SaveFileDialog so the user can save the Image
            // assigned to Button2.
            using (SaveFileDialog saveFileDialog = new SaveFileDialog())
            {
                saveFileDialog.Filter = TheTextOfSaveFileDialogFilter;
                saveFileDialog.Title = TheTextOfSaveFileDialogTitle;
                saveFileDialog.ShowDialog();

                // If the file name is not an empty string open it for saving.
                if (saveFileDialog.FileName != "")
                {
                    String TheFilePath = saveFileDialog.InitialDirectory + saveFileDialog.FileName;
                    switch (saveFileDialog.FilterIndex)
                    {
                        case 1: //default Weka arff file
                            manager.SaveToFile(TheFilePath, false);
                            break;
                        case 2: //filtered Weka arff file
                            manager.SaveToFile(TheFilePath, true);
                            break;
                    }
                }
            }
            popUpMenu.Enabled = true;
        }
    }
}